<?php

class base_shell_loader {

    var $commands_dir;

    var $app_id;

    var $trigger = array();

    var $status = array();

    var $input = array();

    function __construct() {
        set_time_limit(0);
        
        cachemgr::init(false);
        cacheobject::init(false);
        kernel::$console_output = true;
        set_error_handler(array(
            &$this,
            'error_handle'
        ));
        if (file_exists(ROOT_DIR . '/config/config.php')) {
            require (ROOT_DIR . '/config/config.php');
            $this->status['config'] = ROOT_DIR . '/config/config.php';
        } else {
            $this->status['config'] = 'false';
        }
        include (APP_DIR . '/base/defined.php');
        
        // 为shell设置BASE_URL
        if (! defined('BASE_URL')) {
            if ($shell_base_url = app::get('base')->getConf('shell_base_url')) {
                define('BASE_URL', $shell_base_url);
            } else {
                trigger_error('please login backend first!', E_USER_NOTICE);
            }
        }
        
        date_default_timezone_set(defined('DEFAULT_TIMEZONE') ? ('Etc/GMT' . (DEFAULT_TIMEZONE >= 0 ? (DEFAULT_TIMEZONE * - 1) : '+' . (DEFAULT_TIMEZONE * - 1))) : 'UTC');
        $this->buildin_commander = new base_shell_buildin($this);
        ;
    }

    function run() {
        ignore_user_abort(false);
        ob_implicit_flush(1);
        ini_set('implicit_flush', true);
        cachemgr::init(false);
        
        if (strpos(strtolower(PHP_OS), 'win') === 0) {
            if (function_exists('mb_internal_encoding')) {
                mb_internal_encoding("UTF-8");
                mb_http_output("GBK");
                ob_start("mb_output_handler", 2);
            } elseif (function_exists('iconv_set_encoding')) {
                iconv_set_encoding("internal_encoding", "UTF-8");
                iconv_set_encoding("output_encoding", "GBK");
                ob_start("ob_iconv_handler", 2);
            }
        }
        
        if (isset($_SERVER['argv'][1])) {
            $args = array_shift($_SERVER['argv']);
            $rst = $this->exec_command(implode(' ', $_SERVER['argv']));
            if ($rst === false) {
                exit(- 1);
            }
        } else {
            $this->interactive();
        }
    }

    function print_banner() {
        $version = 1.3;
        echo "Ecos shell V{$version} (abort with ^C), Root: ", ROOT_DIR;
    }

    static public function get_width() {
        $output = @strtolower(exec('stty -a |grep columns'));
        if ($output) {
            foreach (explode(';', $output) as $part) {
                if (strpos($part, 'columns') !== false) {
                    return intval(str_replace('columns', '', $part));
                }
            }
        }
        return 80;
    }

    function &get_commander($app_id, $shell_command) {
        $shell_command = strtolower($shell_command);
        if (! $app_id && isset($this->cmdlibs[$shell_command])) {
            return $this->cmdlibs[$shell_command];
        }
        if (file_exists(APP_DIR . '/' . $app_id . '/lib/command/' . $shell_command . '.php')) {
            require_once (APP_DIR . '/' . $app_id . '/lib/command/' . $shell_command . '.php');
            $class_name = $app_id . '_command_' . $shell_command;
            $this->cmdlibs[$shell_command] = new $class_name($this);
            $this->cmdlibs[$shell_command]->app = app::get($app_id);
            return $this->cmdlibs[$shell_command];
        } else {
            $commander = false;
            return $commander;
        }
    }

    function interactive() {
        $this->print_banner();
        $i = 1;
        
        while (true) {
            $line = readline("\n" . ($this->app_id ? ($i++) . ':' . $this->app_id : $i++) . '> ');
            readline_add_history($line);
            
            foreach ($this->trigger as $trigger_name => &$trigger) {
                $trigger->{'begin_' . $trigger_name}($line);
            }
            
            $this->exec($line);
            
            foreach ($this->trigger as $trigger_name => &$trigger) {
                $trigger->{'end_' . $trigger_name}($line);
            }
            // ob_flush();
        }
    }

    function input_option($options, $part, $message = null) {
        // 修改为在浏览器登录下可以执行系统shell安装等命令
        if ($_SERVER['HTTP_USER_AGENT']) {
            // continue
        } else {
            if (PHP_SAPI != 'cli') {
                trigger_error('Can\'t start options editor', E_ERROR);
                exit();
            }
        }
        do {
            echo "\n";
            $output = array();
            $i = 1;
            foreach ($options as $key => $option) {
                $map[$i] = $key;
                
                if (! array_key_exists($key, (array) $this->input[$part]) && array_key_exists('default', $option)) {
                    $this->input[$part][$key] = $option['default'];
                }
                
                if ($option['type'] == 'password') {
                    $current_value = $this->input[$part][$key] ? str_repeat('*', strlen($current_value)) : '(empty)';
                } elseif ($option['type'] == 'select') {
                    if (is_array($option['options_callback'])) {
                        $option['options'] = app::get($option['options_callback']['app'])->runtask($option['options_callback']['method'], $this->input);
                        $options[$key]['options'] = $option['options'];
                    }
                    $current_value = $option['options'][$this->input[$part][$key]];
                } elseif ($this->input[$part][$key] == '') {
                    $current_value = '(empty)';
                } else {
                    $current_value = $this->input[$part][$key];
                }
                
                $output[] = array(
                    str_pad($i, 3, ' ', STR_PAD_LEFT) . '. ' . $option['title'],
                    $current_value
                );
                $i++;
            }
            $this->buildin_commander->output_table($output);
            $line = readline(str_repeat('_', 40) . "\n" . app::get('base')->_('输入项目编号,或输入井号') . '"#"' . app::get('')->_('确认') . ": ");
        } while ($this->save_input_option($options, $map, trim($line), $part));
    }

    function save_input_option($options, $map, $input, $part) {
        if ($input == '#') {
            return false;
        } else {
            $option = $options[$map[$input]];
            switch ($option['type']) {
                
                case 'password':
                    @system('stty -echo');
                    $this->input[$part][$map[$input]] = trim(readline("\n" . app::get('base')->_('输入') . "{$option['title']}: "));
                    @system('stty echo');
                    break;
                
                case 'select':
                    $i = 1;
                    $output = "\n" . app::get('base')->_('选择合适的') . "{$option['title']}:\n";
                    foreach ($options[$map[$input]]['options'] as $k => $v) {
                        $optmap[$i] = $k;
                        $output .= str_pad($i, 3, ' ', STR_PAD_LEFT) . '. ' . $v . "\n";
                        $i++;
                    }
                    $output .= app::get('base')->_("输入合适的") . "{$option['title']}" . app::get('base')->_('编号') . ": ";
                    $this->input[$part][$map[$input]] = $optmap[trim(readline($output))];
                    break;
                
                default:
                    $this->input[$part][$map[$input]] = trim(readline("\n" . app::get('base')->_('输入') . "{$option['title']}: "));
                    break;
            }
            return true;
        }
    }

    function exec_command($line) {
        $this->exec($line);
    }

    function exec($line) {
        try {
            $line = trim($line);
            if (substr($line, - 1, 1) == ';') {
                return $this->buildin_commander->php_call($line);
            } else {
                $command_parts = preg_split("/[\s]+/", $line);
                $shell_command = array_shift($command_parts);
                list ($app_id, $commander) = explode(':', $shell_command);
                if (! $commander) {
                    $commander = $app_id;
                    if (method_exists($this->buildin_commander, 'command_' . $commander)) {
                        array_unshift($command_parts, $shell_command);
                        return $this->buildin_commander->exec($command_parts);
                    } else {
                        echo $shell_command . ": Command not found.";
                    }
                } else {
                    $commander = $this->get_commander($app_id, $commander);
                    if ($commander) {
                        return $commander->exec($command_parts);
                    } else {
                        echo $shell_command . ": Command not found.";
                        return false;
                    }
                }
            }
        } catch (Exception $e) {
            echo 'Error: ', $e->getMessage(), "";
        }
    }

    function error_handle($code, $msg, $file, $line) {
        if ($code == ($code & (E_ERROR ^ E_USER_ERROR ^ E_USER_WARNING))) {
            if ($code == ($code & (E_ERROR ^ E_USER_ERROR))) {
                logger::error(sprintf('ERROR:%d @ %s @ file:%s @ line:%d', $code, $msg, $file, $line));
                exit();
            }
            logger::warning(sprintf('WARNING:%d @ %s @ file:%s @ line:%d', $code, $msg, $file, $line));
        }
        return true;
    }
}

if (! function_exists('readline')) {

    function readline($prompt) {
        echo $prompt;
        // ob_flush();
        $input = '';
        while (1) {
            $key = fgetc(STDIN);
            switch ($key) {
                case "\n":
                    return $input;
                
                default:
                    $input .= $key;
            }
        }
    }

    function readline_add_history($line) {
        // ...
    }

    function readline_completion_function($callback) {}
}
